/*
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "video_decoder.h"
#include "ffpipenode_ohos_mediacodec_vdec.h"


#undef LOG_TAG
#define LOG_TAG "OH_VideoDecoder"

namespace {
constexpr int LIMIT_LOGD_FREQUENCY = 50;
constexpr int ROTATION_ANGLE = 90;
} // namespace

VideoDecoder::~VideoDecoder()
{
    Release();
}

int32_t VideoDecoder::SetCallback(CodecUserData *codecUserData)
{
    LOGE( "swf  OVideoDecoder::SetCallback start \n");
    int32_t ret = AV_ERR_OK;
    ret = OH_VideoDecoder_RegisterCallback(decoder_,
        {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange,
         SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer}, codecUserData);
    if (ret != AV_ERR_OK) {
        LOGE( "swf OH_VideoDecoder_RegisterCallback falied\n");
    }
    LOGE( "swf  OVideoDecoder::SetCallback end \n"); 
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::Config(OH_AVFormat *format, struct EGLNativeWindowType *native_window)
{
    LOGE( "swf  VideoDecoder::Config start \n");
    int ret = OH_VideoDecoder_Configure(decoder_, format);//配置解码器
    OH_AVFormat_Destroy(format);
    format = nullptr;

    OHNativeWindow *window = reinterpret_cast<OHNativeWindow*>(native_window);
    if (window != nullptr) {
        int ret = OH_VideoDecoder_SetSurface(decoder_, window);//送显
        if (ret < 0) {
            LOGE( "swf  OH_VideoDecoder_SetSurface failed \n");
            return -1;
        }
    }
    //decoder_.decInputThread_ = std::make_unique<std::thread>(&VideoDecoder::DecInputThread, this);//绑定输入
    decOutputThread_ = std::make_unique<std::thread>(&VideoDecoder::DecOutputThread, this);//绑定输出

    decContext_ = new CodecUserData;
    SetCallback(decContext_);
 
    {
        int ret = OH_VideoDecoder_Prepare(decoder_);
        if (ret < 0) {
            LOGE( "swf  OH_VideoDecoder_Prepare(decoder_) falied \n");
            return -1;
        }
    }
    LOGE( "swf  VideoDecoder::Config end \n");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::Start()
{
    LOGE( "swf  VideoDecoder::Start() start \n");
    if (OH_VideoDecoder_Start(decoder_) < 0) {
        LOGE( "swf  OH_VideoDecoder_Start failed \n");
        return -1;
    }
    isStarted_ = true;
    LOGE( "swf  VideoDecoder::Start() end \n");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::PushInputData(CodecBufferInfo &info)
{
    LOGE( "swf  VideoDecoder::PushInputData start\n");
    if (OH_AVBuffer_SetBufferAttr(reinterpret_cast<OH_AVBuffer *>(info.buffer), &info.attr) < 0) {
        LOGE( "swf  OH_AVBuffer_SetBufferAttr failed\n");
        return -1;
    }
    if (OH_VideoDecoder_PushInputBuffer(decoder_, info.bufferIndex) < 0) {
        LOGE( "swf  OH_VideoDecoder_PushInputBuffer failed\n");
        return -1;
    }
    LOGE( "swf  VideoDecoder::PushInputData end\n");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::FreeOutputData(uint32_t bufferIndex, bool render)
{
    int32_t ret = AVCODEC_SAMPLE_ERR_OK;
    if (render) {
        ret = OH_VideoDecoder_RenderOutputBuffer(decoder_, bufferIndex);
    } else {
        ret = OH_VideoDecoder_FreeOutputBuffer(decoder_, bufferIndex);
    }
    if (ret < 0) {
        LOGE( "swf  FreeOutputData failed\n");
        return -1;
    }
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::Release()
{
    std::lock_guard<std::mutex> lock(mutex_);
    isStarted_ = false;
    if (decOutputThread_ && decOutputThread_->joinable()) {
        decOutputThread_->detach();
        decOutputThread_.reset();
    }
    if (videoDecoder_ != nullptr) {
        videoDecoder_->Release();
        videoDecoder_.reset();
    }
    if (decContext_ != nullptr) {
        delete decContext_;
        decContext_ = nullptr;
    }
    doneCond_.notify_all();

    if (decoder_ != nullptr) {
        OH_VideoDecoder_Flush(decoder_);
        OH_VideoDecoder_Stop(decoder_);
        OH_VideoDecoder_Destroy(decoder_);
        decoder_ = nullptr;
    }
    
    return AVCODEC_SAMPLE_ERR_OK;
}